// Copyright Epic Games, Inc. All Rights Reserved.

#include "PyShelf.h"

#include "JsonObjectConverter.h"
#include "PyShelfStyle.h"
#include "PyShelfCommands.h"
#include "LevelEditor.h"
#include "STreeItem_File.h"
#include "STreeItem_Folder.h"
#include "Widgets/Docking/SDockTab.h"
#include "Widgets/Layout/SBox.h"
#include "Widgets/Text/STextBlock.h"
#include "ToolMenus.h"
#include "Components/SizeBox.h"
#include "Interfaces/IPluginManager.h"
#include "Widgets/Layout/SScaleBox.h"
#include "Widgets\Views\ITableRow.h"
#include "Widgets\Views\STableViewBase.h"

static const FName PyShelfTabName("PyShelf");

#define LOCTEXT_NAMESPACE "FPyShelfModule"

void FPyShelfModule::StartupModule()
{
	// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module

	// 创建一个 FSlateStyleSet 实例对象，并且设置图标
	FPyShelfStyle::Initialize();
	// 加载图标
	FPyShelfStyle::ReloadTextures();

	// 注册命令
	FPyShelfCommands::Register();

	// 创建当前插件模块自己的命令列表
	PluginCommands = MakeShareable(new FUICommandList);

	// 映射命令行为
	PluginCommands->MapAction(
		FPyShelfCommands::Get().PyShelfWindow,
		FExecuteAction::CreateRaw(this, &FPyShelfModule::PluginButtonClicked),
		FCanExecuteAction());

	// 注册到菜单
	UToolMenus::RegisterStartupCallback(FSimpleMulticastDelegate::FDelegate::CreateRaw(this, &FPyShelfModule::RegisterMenus));

	// 生成窗口界面
	FGlobalTabmanager::Get()->RegisterNomadTabSpawner(PyShelfTabName, FOnSpawnTab::CreateRaw(this, &FPyShelfModule::OnSpawnPluginTab))
		.SetDisplayName(LOCTEXT("FPyShelfTabTitle", "PyShelf"))
		.SetMenuType(ETabSpawnerMenuType::Hidden);
}

void FPyShelfModule::ShutdownModule()
{
	// This function may be called during shutdown to clean up your module.  For modules that support dynamic reloading,
	// we call this function before unloading the module.

	UToolMenus::UnRegisterStartupCallback(this);

	UToolMenus::UnregisterOwner(this);

	FPyShelfStyle::Shutdown();

	FPyShelfCommands::Unregister();

	FGlobalTabmanager::Get()->UnregisterNomadTabSpawner(PyShelfTabName);
}

TSharedRef<SDockTab> FPyShelfModule::OnSpawnPluginTab(const FSpawnTabArgs& SpawnTabArgs)
{
	ReadConfigJson();
	SearchScriptPath();
	
	// 设置标题字体
	FSlateFontInfo titleFont = FCoreStyle::Get().GetFontStyle("EmbossedText");
	titleFont.Size = 32;
	titleFont.OutlineSettings.OutlineSize = 1;
	titleFont.OutlineSettings.OutlineColor = FLinearColor{1.f, 0.501811f, 0.f, 1.f};

	// TreeView Style
	if(!mTreeBrush.IsValid())
	{
		mTreeBrush = MakeShared<FSlateBrush>();
		mTreeBrush->DrawAs = ESlateBrushDrawType::Type::NoDrawType;
	}
	
	mTreeStyle.SetBackgroundBrush(*mTreeBrush);
	
	return SNew(SDockTab)
		.TabRole(ETabRole::NomadTab)
		[
			SNew(SBorder)
			.HAlign(HAlign_Fill)
			.VAlign(VAlign_Fill)
			.BorderImage(new FSlateColorBrush{FLinearColor{0.007499f, 0.007499f, 0.007499f, 1.f}})
			[
				SNew(SVerticalBox)
				// 创建标题
				+SVerticalBox::Slot()
				.Padding(0.f, 0.f)
				.HAlign(HAlign_Fill)
				.VAlign(VAlign_Fill)
				.FillHeight(0.1f)
				[
					SNew(SBorder)
					.BorderImage(new FSlateColorBrush{FLinearColor{0.017642f, 0.017642f, 0.017642f, 1.f}})
					[
						SNew(SHorizontalBox)
						+SHorizontalBox::Slot()
						.Padding(0.f)
						.FillWidth(0.1f)
						.HAlign(HAlign_Fill)
						.VAlign(VAlign_Fill)
						[
							SNew(SSpacer)
						]

						+SHorizontalBox::Slot()
						.Padding(0.f)
						.FillWidth(1.f)
						.HAlign(HAlign_Fill)
						.VAlign(VAlign_Fill)
						[
							SNew(SScaleBox)
							.Stretch(EStretch::ScaleToFit)
							[
								SNew(STextBlock)
								.Text(FText::FromString("PyShelf"))
								.ColorAndOpacity(FLinearColor{0.0f, 0.337662f, 1.f, 1.0f})
								.Font(titleFont)
							]
							.HAlign(HAlign_Fill)
							.VAlign(VAlign_Fill)
						]
					]
					.Padding(0.f, 0.f)
					.HAlign(HAlign_Fill)
					.VAlign(VAlign_Fill)
				]

				// 创建 TreeView
				+SVerticalBox::Slot()
				.Padding(0.f, 5.f, 0.f, 0.f)
				.HAlign(HAlign_Fill)
				.VAlign(VAlign_Fill)
				.FillHeight(1.f)
				[
					SNew(SBorder)
					.BorderImage(new FSlateColorBrush{FLinearColor{1.f, 1.f, 1.f, 0.f}})
					[
						SNew(STreeView<TSharedPtr<FScriptPathItem>>)
						.TreeViewStyle(&mTreeStyle)
						.TreeItemsSource(&mRootModules)
						.ClearSelectionOnClick(false)
						.SelectionMode(ESelectionMode::Single)
						.HighlightParentNodesForSelection(true)
						.OnGetChildren(STreeView<TSharedPtr<FScriptPathItem>>::FOnGetChildren::CreateRaw(
							this, &FPyShelfModule::TreeView_OnGetChildren))
						.OnGenerateRow(STreeView<TSharedPtr<FScriptPathItem>>::FOnGenerateRow::CreateRaw(
							this, &FPyShelfModule::TreeView_OnGenerateRow))
					]
				]
			]
			.Padding(4.f, 2.f)
			.HAlign(HAlign_Fill)
			.VAlign(VAlign_Fill)
		];
}

void FPyShelfModule::ReadConfigJson()
{
	FString cfgPath = IPluginManager::Get().FindPlugin("PyShelf")->GetBaseDir()
									/ TEXT("Content") / TEXT("Python") / TEXT("config.json");

	FString cfgStr;
	FFileHelper::LoadFileToString(cfgStr, *cfgPath);

	// 创建 Json 读取器
	TSharedRef<TJsonReader<>> jsonReader = TJsonReaderFactory<>::Create(cfgStr);

	// 将 Json 读取器反序列化成 FJsonObject
	if (!FJsonSerializer::Deserialize(jsonReader, mCfgJsonRoot))
		mCfgJsonRoot = nullptr;
}

void FPyShelfModule::GetPathAlias(const FString& inPath, FString& outAlias)
{
	if (!mCfgJsonRoot)
		return;

	FString cfgPath = IPluginManager::Get().FindPlugin("PyShelf")->GetBaseDir()
									/ TEXT("Content") / TEXT("Python") / TEXT("/");

	FString relativePath;
	relativePath = inPath.Right(inPath.Len() - cfgPath.Len());
	// 判断字段是否存在
	if (mCfgJsonRoot->HasField(TEXT("pathAlias")))
	{
		TSharedPtr<FJsonObject> pathAlias = mCfgJsonRoot->GetObjectField(TEXT("pathAlias"));
		if (pathAlias->HasField(relativePath))
			outAlias = pathAlias->GetStringField(relativePath);
	}
}

void FPyShelfModule::SearchScriptPath()
{
	FString scriptRoot = IPluginManager::Get().FindPlugin("PyShelf")->GetBaseDir() / TEXT("Content") / TEXT("Python");
	//UE_LOG(LogTemp, Display, TEXT("scriptRoot: %s"), *scriptRoot);

	// 搜索根路径下的模块
	TArray<FString> modules;
	IFileManager::Get().FindFiles(modules, *(scriptRoot / TEXT("*")), false, true);

	// 递归搜索模块下的 "main.py"
	for(auto& module:modules)
	{
		//FString moduleRoot = scriptRoot / module / TEXT("*");
		//UE_LOG(LogTemp, Display, TEXT("moduleRoot: %s"), *(scriptRoot / module / TEXT("*")));

		TSharedPtr<FScriptPathItem> childItem = MakeShared<FScriptPathItem>(scriptRoot / module);
		GetPathAlias(childItem->mPath, childItem->mAlias);
		mRootModules.Add(childItem);
		if(!SearchScriptPathRecursive(childItem))
			mRootModules.Pop();
	}
}

bool FPyShelfModule::SearchScriptPathRecursive(TSharedPtr<FScriptPathItem>& inRoot)
{
	FString rootPath = inRoot->mPath;
	int8_t currentLevel = inRoot->mLevel + 1;
	TArray<FString> foundedFiles;
	TArray<FString> foundedDirs;
	bool currentLevelResult = false;
	
	//UE_LOG(LogTemp, Display, TEXT("rootPath: %s"), *rootPath);
	
	// 搜索 "main.py"
	IFileManager::Get().FindFiles(foundedFiles, *rootPath, TEXT("*.py"));
	for(auto& foundedFile:foundedFiles)
	{
		//UE_LOG(LogTemp, Display, TEXT("file: %s"), *(inRoot->mPath / foundedFile));
		if (foundedFile == "main.py")
		{
			currentLevelResult = true;
			
			TSharedPtr<FScriptPathItem> childItem = MakeShared<FScriptPathItem>(inRoot->mPath / foundedFile);
			childItem->mLevel = currentLevel;
			GetPathAlias(childItem->mPath, childItem->mAlias);
			
			inRoot->mChildren.Add(childItem);
		}
	}

	// 搜索子文件夹
	IFileManager::Get().FindFiles(foundedDirs, *(rootPath / TEXT("*")), false, true);
	for(auto& foundedDir:foundedDirs)
	{
		//UE_LOG(LogTemp, Display, TEXT("dir: %s"), *(inRoot->mPath / foundedDir));
		
		TSharedPtr<FScriptPathItem> childItem = MakeShared<FScriptPathItem>(inRoot->mPath / foundedDir);
		childItem->mLevel = currentLevel;
		GetPathAlias(childItem->mPath, childItem->mAlias);
		
		inRoot->mChildren.Add(childItem);
		if(SearchScriptPathRecursive(childItem))
			currentLevelResult = true;
		else
			inRoot->mChildren.Pop();
	}

	return currentLevelResult;
}

void FPyShelfModule::TreeView_OnGetChildren(TSharedPtr<FScriptPathItem> Item, TArray<TSharedPtr<FScriptPathItem>>& OutChildren)
{
	OutChildren = Item->mChildren;
}

TSharedRef<ITableRow> FPyShelfModule::TreeView_OnGenerateRow(TSharedPtr<FScriptPathItem> Item,
	const TSharedRef<STableViewBase>& OwningWidget)
{

	// 设置 Tree Row Style
	// 设置奇数行
	mRowStyle.SetOddRowBackgroundBrush(mColorBrush_Normal);
	mRowStyle.SetOddRowBackgroundHoveredBrush(mColorBrush_Normal);
	// 设置偶数行
	mRowStyle.SetEvenRowBackgroundBrush(mColorBrush_Normal);
	mRowStyle.SetEvenRowBackgroundHoveredBrush(mColorBrush_Normal);
	// 设置被选中行
	// 被选行的文本颜色
	//mRowStyle.SetSelectedTextColor(FLinearColor{1.f, 0.502887f, 0.f, 1.f});
	// 被选中行的 brush
	mRowStyle.SetActiveBrush(mColorBrush_Normal);
	// 被选中行被鼠标悬浮时的 brush
	mRowStyle.SetActiveHoveredBrush(mColorBrush_Normal);
	// 被选中行的父级 brush
	mRowStyle.SetActiveHighlightedBrush(mColorBrush_Normal);
	// 被选中行失去焦点时的 brush （当选中行且点击 TreeView以外的控件时）
	mRowStyle.SetInactiveBrush(mColorBrush_Normal);
	// 被选中行失去焦点后，又被鼠标悬浮时的 brush
	mRowStyle.SetInactiveHoveredBrush(mColorBrush_Normal);
	// 被选中行失去焦点时父级的 brush
	mRowStyle.SetInactiveHighlightedBrush(mColorBrush_Normal);

	TSharedPtr<SWidget> newItem;
	if (Item->bIsFolder)
	{
		newItem = CreateTreeItem_Folder(Item);
	}
	else
	{
		newItem = CreateTreeItem_File(Item);
	}
	
	return SNew(STableRow<TSharedPtr<FString>>, OwningWidget)
		.Style(&mRowStyle)
		[
			newItem.ToSharedRef()
		];
}

TSharedRef<SWidget> FPyShelfModule::CreateTreeItem_Folder(const TSharedPtr<FScriptPathItem>& Item)
{
	float fillRatioTopBottom = 2.f;
	float fillRatioLeftRight = (Item->mLevel + 1) * 5.f;

	FSlateFontInfo font = FCoreStyle::Get().GetFontStyle("EmbossedText");
	
	return SNew(STreeItem_Folder)
		.widthOverride(200.f)
		.heightOverride(60.f)
		.fillRatioTopBottom(fillRatioTopBottom)
		.fillRatioLeftRight(fillRatioLeftRight)
		.itemText(FString::Printf(TEXT("%s (%d)"), *Item->mAlias, Item->mChildren.Num()));
}

TSharedRef<SWidget> FPyShelfModule::CreateTreeItem_File(const TSharedPtr<FScriptPathItem>& Item)
{
	return SNew(STreeItem_File)
		.widthOverride(50.f)
		.heightOverride(50.f)
		.fillRatioTopBottom(1.f)
		.fillRatioLeftRight(50.f)
		.buttonText(Item->mAlias)
		.scriptPath(Item->mPath);
}

void FPyShelfModule::PluginButtonClicked()
{
	TSharedPtr<SDockTab> existingLiveTab = FGlobalTabmanager::Get()->FindExistingLiveTab(PyShelfTabName);
	if(existingLiveTab.IsValid())
		return;

	mRootModules.Empty();
	FGlobalTabmanager::Get()->TryInvokeTab(PyShelfTabName);
}

void FPyShelfModule::RegisterMenus()
{
	// Owner will be used for cleanup in call to UToolMenus::UnregisterOwner
	FToolMenuOwnerScoped OwnerScoped(this);

	{
		UToolMenu* Menu = UToolMenus::Get()->ExtendMenu("LevelEditor.MainMenu.Tools");
		{
			FToolMenuSection& Section = Menu->FindOrAddSection("Python");
			Section.AddMenuEntryWithCommandList(FPyShelfCommands::Get().PyShelfWindow, PluginCommands);
		}
	}
}

#undef LOCTEXT_NAMESPACE
	
IMPLEMENT_MODULE(FPyShelfModule, PyShelf)